package com.hhy.lombok.processor;


import com.hhy.lombok.annotation.Builder;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;

import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.lang.annotation.Annotation;
import java.util.Set;

import static com.hhy.lombok.utils.JCUtils.getJClassFields;
import static com.hhy.lombok.utils.ProcessUtils.*;
import static com.hhy.lombok.constant.Constant.*;

@SupportedAnnotationTypes("com.hhy.lombok.annotation.Builder")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class BuilderProcessor extends BaseProcessor {

    private Name builderClassName;

    @Override
    public void doProcess(Element element) {
        JCTree jcTree = trees.getTree(element);
        jcTree.accept(new TreeTranslator() {
            @Override
            public void visitClassDef(JCTree.JCClassDecl jcClass) {
                treeMaker.at(jcClass.pos);
                builderClassName = names.fromString((jcClass.name.toString()) + "Builder");
                // 添加builder方法
                jcClass.defs = jcClass.defs.append(
                        createStaticBuilderMethod(jcClass)
                );
                // 添加静态内部类
                jcClass.defs = jcClass.defs.append(
                        createJCClass(jcClass)
                );
                builderClassName = null;
                log.log(jcClass, "generated builder inner class");
            }
        });
    }


    /**
     * 创建 builder方法，返回静态内部类的实例
     *
     * @return builder方法的语法树节点
     */
    private JCTree.JCMethodDecl createStaticBuilderMethod(JCTree.JCClassDecl jcClass) {
        ListBuffer<JCTree.JCStatement> jcStatements = new ListBuffer<>();
        // 添加Builder模式中的返回语句 " return new XXXBuilder(); "
        jcStatements.append(
                treeMaker.Return(
                        treeMaker.NewClass(
                                null,
                                List.nil(),
                                treeMaker.Ident(this.builderClassName),
                                List.nil(),
                                null
                        )
                )
        );

        JCTree.JCBlock jcBlock = treeMaker.Block(
                0 //访问标志
                , jcStatements.toList() //所有的语句
        );

        return treeMaker.MethodDef(
                treeMaker.Modifiers(Flags.PUBLIC + Flags.STATIC),
                names.fromString(BUILDER_STATIC_METHOD_NAME),
                treeMaker.Ident(this.builderClassName), //返回类型
                List.nil(),
                List.nil(),
                List.nil(),
                jcBlock,
                null
        );
    }

    /**
     * 创建一个类的语法树节点。作为Builder模式中的Builder类
     *
     * @return 创建出来的类的语法树节点
     */
    private JCTree.JCClassDecl createJCClass(JCTree.JCClassDecl jcClass) {

        ListBuffer<JCTree> jcTrees = new ListBuffer<>();

        jcTrees.appendList(createVariables(jcClass));
        jcTrees.appendList(createSetJCMethods(jcClass));
        jcTrees.append(createBuildJCMethod(jcClass));

        return treeMaker.ClassDef(
                treeMaker.Modifiers(Flags.PUBLIC + Flags.STATIC + Flags.FINAL),
                builderClassName,
                List.nil(),
                null,
                List.nil(),
                jcTrees.toList());
    }

    /**
     * 根据方法集合创建对应的字段的语法树节点集合
     *
     * @return 静态内部类的字段的语法树节点集合
     */
    private List<JCTree> createVariables(JCTree.JCClassDecl jcClass) {
        ListBuffer<JCTree> jcVariables = new ListBuffer<>();

        for (JCTree.JCVariableDecl fieldJCVariable : getJClassFields(jcClass)) {
            jcVariables.append(
                    treeMaker.VarDef(
                            treeMaker.Modifiers(Flags.PRIVATE),
                            names.fromString((fieldJCVariable.name.toString())),
                            fieldJCVariable.vartype,
                            null
                    ));
        }

        return jcVariables.toList();
    }

    /**
     * 创建方法的语法树节点的集合。作为Builder模式中的setXXX方法
     *
     * @return 方法节点集合
     */
    private List<JCTree> createSetJCMethods(JCTree.JCClassDecl jcClass) {
        ListBuffer<JCTree> setJCMethods = new ListBuffer<>();

        for (JCTree.JCVariableDecl fieldJCVariable : getJClassFields(jcClass)) {
            setJCMethods.append(createSetJCMethod(fieldJCVariable));
        }

        return setJCMethods.toList();
    }

    /**
     * 创建一个方法的语法树节点。作为Builder模式中的setXXX方法
     *
     * @return 方法节点
     */
    private JCTree.JCMethodDecl createSetJCMethod(JCTree.JCVariableDecl jcVariable) {
        ListBuffer<JCTree.JCStatement> jcStatements = new ListBuffer<>();
        //添加语句 " this.xxx = xxx; "
        jcStatements.append(
                treeMaker.Exec(
                        treeMaker.Assign(
                                treeMaker.Select(
                                        treeMaker.Ident(names.fromString(THIS)), names.fromString(jcVariable.name.toString())
                                ),
                                treeMaker.Ident(names.fromString(jcVariable.name.toString()))
                        )
                )
        );
        // 添加Builder模式中的返回语句 " return this; "
        jcStatements.append(
                treeMaker.Return(treeMaker.Ident(names.fromString(THIS)))
        );
        JCTree.JCBlock jcBlock = treeMaker.Block(
                0
                , jcStatements.toList()
        );
        return treeMaker.MethodDef(
                treeMaker.Modifiers(Flags.PUBLIC),
                names.fromString(jcVariable.name.toString()),
                treeMaker.Ident(builderClassName),
                List.nil(),
                List.of(cloneJCVariableAsParam(treeMaker, jcVariable)),
                List.nil(),
                jcBlock,
                null
        );
    }

    /**
     * 创建build方法的语法树节点
     *
     * @return build方法的语法树节点
     */
    private JCTree.JCMethodDecl createBuildJCMethod(JCTree.JCClassDecl jcClass) {
        ListBuffer<JCTree.JCExpression> jcVariableExpressions = new ListBuffer<>();

        for (JCTree.JCVariableDecl jcVariable : getJClassFields(jcClass)) {
            jcVariableExpressions.append(
                    treeMaker.Select(
                            treeMaker.Ident(names.fromString(THIS)),
                            names.fromString(jcVariable.name.toString())
                    )
            );
        }

        ListBuffer<JCTree.JCStatement> jcStatements = new ListBuffer<>();

        //添加返回语句 " return new XXX(arg1, arg2, ...); "
        jcStatements.append(
                treeMaker.Return(
                        treeMaker.NewClass(
                                null,
                                List.nil(),
                                treeMaker.Ident(names.fromString(jcClass.name.toString())),
                                jcVariableExpressions.toList(), //参数列表
                                null
                        )
                )
        );

        JCTree.JCBlock jcBlock = treeMaker.Block(
                0 //访问标志
                , jcStatements.toList() //所有的语句
        );

        return treeMaker.MethodDef(
                treeMaker.Modifiers(Flags.PUBLIC),
                names.fromString(BUILD_METHOD_NAME),
                treeMaker.Ident(names.fromString(jcClass.name.toString())), //返回类型
                List.nil(),
                List.nil(),
                List.nil(),
                jcBlock,
                null
        );
    }

    @Override
    protected Class<? extends Annotation> getNeedToHandleAnnotation() {
        return Builder.class;
    }
}